package com.syxbyi.hmwk2;

import com.syxbyi.hmwk1.main.Gobang;

import java.io.*;
import java.util.Objects;
import java.util.Scanner;

/**
 * 编程实现控制台版并支持两人对战的五子棋游戏。
 *
 * （1）绘制棋盘 - 写一个成员方法实现
 *
 * （2）提示黑方和白方分别下棋并重新绘制棋盘 - 写一个成员方法实现。
 *
 * （3）每当一方下棋后判断是否获胜 - 写一个成员方法实现。
 *
 * （4）提示： 采用二维数组来模拟并描述棋盘
 */
public class GobangPro extends Gobang {
    private boolean blackTurn = true;
    private InputStream inputStream = System.in;
    private Scanner scanner = new Scanner(inputStream);

    public GobangPro(){
        super();
        go();
    }

    // 支持从其他输入流中读取数据，方便测试
    public GobangPro(InputStream inputStream) {
        super();
        this.inputStream = inputStream;
        scanner = new Scanner(inputStream);
        go();
    }

    public void go() {
        printBoard();
        String input;
        while (true) {
            System.out.printf("欢迎体验五子棋小游戏，输入quit退出。请%s色方下棋（格式例如a3，先输入行名称再输入列名称）：",
                    blackTurn ? "黑" : "白");
            // 输入quit退出游戏
            if ((input = scanner.next()).equals("quit")) {
                break;
            }
            // 如果从其他输入流读取，则需要打印
            if (inputStream != System.in) {
                System.out.println(input);
            }
            boolean victory = set(input.charAt(0), input.charAt(1));
            printBoard();
            if (victory) {
                System.out.printf("恭喜%s色方赢了！", blackTurn ? "黑" : "白");
                break;
            }
        }
        System.out.println("Bye");
    }

    /**
     * 如果当前落子结束游戏，则返回true
     * @param r 当前落子的行坐标
     * @param c 当前落子的列坐标
     * @return 游戏是否结束
     */
    public boolean set(int r, int c) {
        // input check
        Objects.checkIndex(r-1, ROW);
        Objects.checkIndex(c-1, COL);
        if (board[r][c] != '+') {
            System.out.println("不会吧不会吧，不会真的有人会把五子棋放在其他棋子上吧？现在上天给了你一次重新下棋的机会，赶紧珍惜吧");
            return false;
        }
        board[r][c] = blackTurn ? 'X' : 'O';
        if (victory(r, c)) {
            return true;
        }
        blackTurn = !blackTurn;
        return false;
    }

    /**
     * 检查输入，将玩家输入字符转为棋盘上的 index
     * TODO 我觉得可以和 index 转 header 写一起复用一下
     * @param r 玩家输入的第一个字符，代表行数
     * @param c 输入的第二个字符，代表列数
     * @return 如果当前落子结束游戏，则返回true
     */
    public boolean set(char r, char c) {
        // row, col from 1-16
        // 因为整个棋盘是 17×17 第 0 行 0 列是 header
        int row = 0, col = 0;
        for (int i = 1; i < header.length; i++) {
            if (header[i] == r) {
                row = i;
            }
            if (header[i] == c) {
                col = i;
            }
        }
        if (row == 0 || col == 0) {
            System.out.println("啊这，输入错了啦");
            return false;
        }
        return set(row, col);
    }

    /**
     * 从指定位置开始，向某个方向前进直到没有己方棋子
     * @param r 起点行坐标
     * @param c 起点列坐标
     * @param rOff 每次前进的行数 {-1, 0, 1}
     * @param cOff 每次前进的列数 {-1, 0, 1}
     * @return 遍历发现的棋子数量
     */
    public int look(int r, int c, int rOff, int cOff) {
        int s = 0;
        while (true) {
            r += rOff;
            c += cOff;
            if (r > 0 && r < 17 && c > 0 && c < 17 && board[r][c] == (blackTurn ? 'X' : 'O')) {
                s += 1;
                continue;
            }
            break;
        }
        return s;
    }

    // 暴力判别法，每下一个子都往8个方向计算一遍
    public boolean victory(int r, int c) {
        // left-top, up, right-top, right
        int[] rOffset = {-1, -1, -1, 0};
        int[] cOffset = {-1, 0, 1, 1};
        int s = 1;
        for (int i = 0; i < 4; i++) {
            // 从当前棋子开始，查找 左上/上/右上/右 边有多少颗相同棋子
            s += look(r, c, rOffset[i], cOffset[i]);
            // 查找一条直线上的另一方向有多少棋子
            s += look(r, c, - rOffset[i], - cOffset[i]);
            if (s >= 5) {
                return true;
            }
            s = 1;
        }
        return false;
    }

    public static void main(String[] args) {
//        GobangPro gobangPro = new GobangPro();
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(new File("data" + File.separator + "gobang.txt"));
            GobangPro gobangPro = new GobangPro(fileInputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
